package sim;


import java.io.*;
import java.util.Scanner;

/**
 * The main simulator class invoked from the command line.
 * @author Piotr
 * 
 */
public class Simulator {

	static IFStage ifs;
	static IDStage ids;
	static EXStage exs;
	static MEMStage mems;
	static WBStage wbs;
	static HazardController hzc;
	
	static final int maxCycleCount = 100000;
	
	/**
	 * Main method. Constructs all necessary objects and controls execution.
	 * @param args	Filename of program to be ran in the simulator.
	 * @
	 */
	public static void main(String[] args) throws IOException, InvalidInstructionException {
		
		
		File programFile;
		File dataFile = null;
		
		if (args.length > 0) {
			programFile = new File(args[0]);
			if (!programFile.exists()) {
				System.out.println("File \"" + args[0] + "\" does not exist");
				System.exit(1);
			}
			if (args.length > 1) {
				dataFile = new File(args[1]);
				if (!dataFile.exists()) {
					System.out.println("File \"" + args[0] + "\" does not exist");
					System.exit(1);
				}
			}
			// Create the pipeline objects
			ifs = new IFStage(0, programFile);
			exs = new EXStage();
			wbs = new WBStage();
			hzc = new HazardController();
			if (dataFile != null) {
				ids = new IDStage(dataFile);
				mems = new MEMStage(dataFile);
			} else {
				ids = new IDStage();
				mems = new MEMStage();
			}
			int instrsInPipe = 0;
			int cycleCount = 0;
			int instrCount = 0;
			boolean programDone = false;
			while(!programDone && cycleCount < maxCycleCount) {
				instrsInPipe = 0;
				cycleCount++;
				//System.out.println("####################### Cycle " + cycleCount + "\t#######################");
				if (wbs.cycle() == 1) {
					instrsInPipe ++;
					instrCount ++;
				}
				instrsInPipe += mems.cycle();
				instrsInPipe += exs.cycle();
				instrsInPipe += ids.cycle();
				instrsInPipe += ifs.cycle();
				/*
				System.out.println("*************** IF Stage Instruction: ***************");
				System.out.println(ifs.getInstruction());
				System.out.println("*************** ID Stage Instruction: ***************");
				System.out.println(ids.getInstruction());
				System.out.println("*************** EX Stage Instruction: ***************");
				System.out.println(exs.getInstruction());
				System.out.println("*************** MEM Stage Instruction: **************");
				System.out.println(mems.getInstruction());
				System.out.println("*************** WB Stage Instruction: ***************");
				System.out.println(wbs.getInstruction());
				System.out.println("Instructions in pipeline: " + instrsInPipe);
				*/
				Instruction wbInstr = wbs.getInstruction();
				if (wbInstr != null) {
					String tag = wbInstr.textString;
					printOutput(tag, cycleCount, instrsInPipe);
				}
				if (ifs.isProgramFinished() && instrsInPipe == 0) {
					programDone = true;
					System.out.println("###################### Program Completed ######################");
					System.out.println("Statistics:");
					System.out.println("Total instructions executed: " + instrCount);
					System.out.println("Total clock cycles: " + (cycleCount - 1));
					System.out.println("Average CPI: " + (float)(cycleCount - 1)/instrCount);
					System.out.println(ids.dumpRegisters());
					System.out.println(mems.dumpMemory());
				}
				
			}

		} else {
			System.out.println("No input file specified");
			System.exit(1);
		}
				

	}
	
	static private void printOutput(String tag, int cycleCount, int instrsInPipe) {
		//System.out.println(tag);
		Scanner sc = new Scanner(tag);
		String match = sc.findInLine("^![ipmr]+");
		if (match != null) {
			//System.out.println(match);
			sc = new Scanner(match);
			System.out.println("####################### Cycle " + cycleCount + "\t#######################");
			if (match.contains("p")) {
				System.out.println("*************** IF Stage Instruction: ***************");
				System.out.println(ifs.getInstruction());
				System.out.println("*************** ID Stage Instruction: ***************");
				System.out.println(ids.getInstruction());
				System.out.println("*************** EX Stage Instruction: ***************");
				System.out.println(exs.getInstruction());
				System.out.println("*************** MEM Stage Instruction: **************");
				System.out.println(mems.getInstruction());
				System.out.println("*************** WB Stage Instruction: ***************");
				System.out.println(wbs.getInstruction());
				System.out.println("Instructions in pipeline: " + instrsInPipe);
			} else if (match.contains("i")) {
				System.out.println("*************** WB Stage Instruction: ***************");
				System.out.println(wbs.getInstruction());
			}
			if (match.contains("r")) {
				System.out.println(ids.dumpRegisters());
			}
			if (match.contains("m")) {
				System.out.println(mems.dumpMemory());
			}
		}
	}

}
